/**
 *
 *  Logger.h
 *  An Tao
 *
 *  Public header file in trantor lib.
 *
 *  Copyright 2018, An Tao.  All rights reserved.
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the License file.
 *
 *
 */

#pragma once

#include <trantor/utils/NonCopyable.h>
#include <trantor/utils/Date.h>
#include <trantor/utils/LogStream.h>
#include <string.h>
#include <functional>
#include <iostream>
#include "spdlog/spdlog.h"

namespace trantor {

    typedef std::shared_ptr <spdlog::logger> LoggerPtr;
    LoggerPtr getLogger(const std::string& name, int logLevel, bool logToFile = false);

    class Logger : public NonCopyable {
      public:
        enum LogLevel {
            kTrace = 0,
            kDebug,
            kInfo,
            kWarn,
            kError,
            kFatal,
            kNumberOfLogLevels
        };

        Logger(const char* file, int line);
        Logger(const char* file, int line, LogLevel level);
        Logger(const char* file, int line, bool isSysErr);
        Logger(const char* file, int line, LogLevel level, const char *func);
        ~Logger();
        LogStream &stream();

        static void setLogLevel(LogLevel level) {
            logLevel_() = level;
        }
        static LogLevel logLevel() {
            return logLevel_();
        }

      protected:
        static LogLevel &logLevel_() {
#ifdef RELEASE
            static LogLevel logLevel = LogLevel::kTrace;
#else
            static LogLevel logLevel = LogLevel::kTrace;
#endif
            return logLevel;
        }

        LogStream logStream_;
        Date date_{Date::now()};
        int fileLine_;
        LogLevel level_;
        spdlog::source_loc source_loc_;
    };


    class LoggerInst {
      protected:
        LoggerInst() {}
        ~LoggerInst() {}

      public:
        static LoggerInst& instance();
        LoggerPtr getLogger();
        LoggerPtr createLogger(const std::string& name, int logLevel, bool showConsole, bool defLogger = false);

      private:
        LoggerPtr m_defLogger;
    };

#define LOG_TRACE                                                          \
    if (trantor::Logger::logLevel() <= trantor::Logger::kTrace)            \
    trantor::Logger(SPDLOG_FILE_BASENAME(__FILE__), __LINE__, trantor::Logger::kTrace, __func__).stream()
#define LOG_DEBUG                                                          \
    if (trantor::Logger::logLevel() <= trantor::Logger::kDebug)            \
    trantor::Logger(SPDLOG_FILE_BASENAME(__FILE__), __LINE__, trantor::Logger::kDebug, __func__).stream()
#define LOG_INFO                                               \
    if (trantor::Logger::logLevel() <= trantor::Logger::kInfo) \
    trantor::Logger(SPDLOG_FILE_BASENAME(__FILE__), __LINE__).stream()
#define LOG_WARN \
    trantor::Logger(SPDLOG_FILE_BASENAME(__FILE__), __LINE__, trantor::Logger::kWarn).stream()
#define LOG_ERROR \
    trantor::Logger(SPDLOG_FILE_BASENAME(__FILE__), __LINE__, trantor::Logger::kError).stream()
#define LOG_FATAL \
    trantor::Logger(SPDLOG_FILE_BASENAME(__FILE__), __LINE__, trantor::Logger::kFatal).stream()
#define LOG_SYSERR trantor::Logger(__FILE__, __LINE__, true).stream()

    const char *strerror_tl(int savedErrno);



}  // namespace trantor


#define LOGGER_CALL(logger, level, ...)   \
    if (logger && logger->should_log(level))     \
        logger->log(spdlog::source_loc{SPDLOG_FILE_BASENAME(__FILE__), __LINE__, ""}, level, __VA_ARGS__)

#define LOG_TRACE_FMT(logger,  ...) LOGGER_CALL(logger, spdlog::level::trace, __VA_ARGS__);
#define LOG_DEBUG_FMT(logger,  ...) LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__);
#define LOG_INFO_FMT(logger,  ...) LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__);
#define LOG_WARN_FMT(logger,  ...) LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__);
#define LOG_ERROR_FMT(logger,  ...) LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__);
#define LOG_CRITICAL_FMT(logger,  ...) LOGGER_CALL(logger, spdlog::level::critical, __VA_ARGS__);

#define LTrace(...)  LOG_TRACE_FMT(trantor::LoggerInst::instance().getLogger(),__VA_ARGS__)
#define LDebug(...)  LOG_DEBUG_FMT(trantor::LoggerInst::instance().getLogger(),__VA_ARGS__)
#define LInfo(...)  LOG_INFO_FMT(trantor::LoggerInst::instance().getLogger(),__VA_ARGS__)
#define LWarn(...) LOG_WARN_FMT(trantor::LoggerInst::instance().getLogger(),__VA_ARGS__)
#define LError(...)  LOG_ERROR_FMT(trantor::LoggerInst::instance().getLogger(),__VA_ARGS__)
#define LCritical(...)  LOG_CRITICAL_FMT(trantor::LoggerInst::instance().getLogger(),__VA_ARGS__)
